﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using System.ComponentModel;
using FinPlusCommon;
using log = Logger.Logger;
using u = FinPlusUtility.Utility;

namespace FinPlusComponents
{
    public class FinPlusQuoteSources : ConcurrentDictionary<string, FinPlusQuoteSource> { }

    public class FinPlusQuoteSource :FinPlusComponent
    {
        public override string Id { get; set; }
        public override string[] States { get { return new string[] { }; } }
        public override string[] TestStates { get { return new string[] { }; } }
        public override IFinPlusFunctions Model { get { return null; } }
        public override string ModelTest { get { return null; } }
        public override bool IsValid { get; set; }

        public FinPlusQuoteBuySells BuySells { get; private set; }
        public bool IsOn { get { return bool.Parse(_isOnOff.Value); } }
        public string SourceName { get; private set; }
        public string Instrument { get; private set; }
        
        private enum Params { Instrument, Buy, Sell, BuySell, Spread, SpreadType, Trader, QuoteSource }
        
        private FinPlusQuoteSpreadCallbacks _spreadCallbacks;
        private FinPlusQuoteSpreads _spreads;
        private readonly IFinPlusFunction _instrument;

        //extra fields
        //private Item _error;
        private IFinPlusItem _isOnOff;

        //construct
        public FinPlusQuoteSource(IFinPlusFunction instrument, string sourceName, Action<IFinPlusFunction> publish = null, FinPlusQuoteSources platforms = null)
        {
            _instrument = instrument;
            Instrument = instrument[Params.Instrument.ToString()].ToString();
            SourceName = sourceName;
            BuySells = new FinPlusQuoteBuySells();
            _isOnOff = instrument.Add(SourceName, false, SourceName);
            _isOnOff.PropertyChanged += OnOff_Changed;

            BuySells.TryAdd(Params.Buy.ToString(), new FinPlusQuoteBuySell(_instrument, Params.Buy.ToString(), SourceName, publish, platforms, this));
            BuySells.TryAdd(Params.Sell.ToString(), new FinPlusQuoteBuySell(_instrument, Params.Sell.ToString(), SourceName, publish, platforms, this));
            //_error = instrument.Add(string.Format("{0}Error", SourceName), "Ok", SourceName);
        }

        //common control interface
        public override void Dock(object component, string dock)
        {
            IsValid = false;
            throw new NotImplementedException();
        }

        public override void Initialise()
        {

        }

        public override void CommandRouter(IFinPlusFunction func) { }

        public override void Dispose()
        {
            Stop();
            foreach (var buySell in BuySells.Values)
                buySell.Dispose();
        }

        //public
        public void AddSpreads(IFinPlusFunction[] spreads, FinPlusQuoteInstruments quoteInstruments, FinPlusQuoteInstrument quoteInstrument)
        {
            try
            {
                if (_spreads == null) _spreads = new FinPlusQuoteSpreads();
                var traderName = string.Empty;

                foreach (var spread in spreads)
                {
                    var spreadTo = spread[Params.Spread.ToString()].ToString();
                    var spreadType = spread[Params.SpreadType.ToString()].ToString();
                    var spreadWrapper = new FinPlusQuoteSpread(_instrument, spread);
                    traderName = spread[Params.Trader.ToString()].ToString();

                    spreadWrapper.SpreadChanged += Spread_Changed;
                    _spreads[spreadTo] = spreadWrapper;

                    if (!string.IsNullOrEmpty(spreadTo))
                    {
                        if (spreadType.Equals(Params.Instrument.ToString()) && quoteInstruments.ContainsKey(spreadTo))
                            quoteInstruments[spreadTo].LinkSpread(Instrument, Spread);
                        else if (spreadType.Equals(Params.QuoteSource.ToString()))
                            quoteInstrument.Sources[spreadTo].LinkSpread(SourceName, Spread);
                    }
                }

                _instrument.Add(Params.Trader.ToString(), traderName, traderName);
            }
            catch (Exception e)
            {
                //managed error
                Error(e.Message);
                throw;
            }
        }

        public void AddQuoteInterests(IFinPlusFunction[] quoteInterests)
        {
            foreach (var buySell in BuySells.Values)
                buySell.AddQuoteInterests(quoteInterests.Where(f => f[Params.BuySell.ToString()].ToString().Equals(buySell.BuySell)).ToArray<IFinPlusFunction>());
        }

        public void LinkSpread(string sourceName, Action<string, FinPlusQuoteWrapper, string> action)
        {
            if (_spreadCallbacks == null) _spreadCallbacks = new FinPlusQuoteSpreadCallbacks();
            _spreadCallbacks[sourceName] = action;
        }

        public void Error(string message)
        {
            Stop();
            //_error.SetValue(message);
        }

        //quote
        public bool Quote(FinPlusQuoteWrapper quote, string source)
        {
            try
            {
                //update best source quote
                bool hasUpdated = false;
                FinPlusQuoteBuySell buySell;
                if (BuySells.TryGetValue(quote.BuySell, out buySell))
                    hasUpdated = buySell.Quote(quote, source);

                //update trader quote related spreads
                if (_spreadCallbacks != null && hasUpdated)
                    foreach (var spreadCallback in _spreadCallbacks.Values)
                        spreadCallback(source, buySell.BestQuote, quote.BuySell);

                return hasUpdated;
            }
            catch (Exception e)
            {
                //managed error
                Error(e.Message);
                throw;
            }
        }

        public bool QuoteCancel(FinPlusQuoteWrapper quote, string source)
        {
            try
            {
                //update best source quote
                bool hasUpdated = false;
                FinPlusQuoteBuySell buySell;
                if (BuySells.TryGetValue(quote.BuySell, out buySell))
                    hasUpdated = buySell.QuoteCancel(quote, source);

                //update trader quote related spreads
                if (_spreadCallbacks != null && hasUpdated)
                    foreach (var spreadCallback in _spreadCallbacks.Values)
                        spreadCallback(source, buySell.BestQuote, quote.BuySell);

                return hasUpdated;
            }
            catch (Exception e)
            {
                //managed error
                Error(e.Message);
                throw;
            }
        }

        public void QuoteExecutePublish(FinPlusQuoteWrapper quote)
        {
            FinPlusQuoteBuySell buySell;
            if (BuySells.TryGetValue(quote.BuySell, out buySell))
                buySell.QuoteExecutePublish(quote);
        }

        public void Spread(string key, FinPlusQuoteWrapper bestQuote, string buySell)
        {
            try
            {
                //update trader spread best quote
                FinPlusQuoteBuySell quoteBuySell;
                FinPlusQuoteSpread spread = null;
                if (_spreads.ContainsKey(key))
                    spread = _spreads[key];

                if (BuySells.TryGetValue(buySell, out quoteBuySell))
                    quoteBuySell.Spread(bestQuote, spread);
            }
            catch (Exception e)
            {
                //managed error
                Error(e.Message);
                throw;
            }
        }

        public FinPlusQuoteWrapper BestQuote(string buySell)
        {
            FinPlusQuoteBuySell quoteBuySell = null;
            if (BuySells.TryGetValue(buySell, out quoteBuySell))
                return quoteBuySell.BestQuote;

            return null;
        }

        public void Stop()
        {
            _isOnOff.Set(false);
            foreach (var buySell in BuySells.Values)
                buySell.QuoteCancelPublish();
        }

        public bool IsReady()
        {
            return _spreads.IsReady();
        }

        //events
        private void Spread_Changed(object sender, PropertyChangedEventArgs a)
        {
            Stop();
        }
        private void OnOff_Changed(object sender, PropertyChangedEventArgs a)
        {
            if (_spreads != null && (bool)_isOnOff.Object && !_spreads.IsReady())
                _isOnOff.Set(false);
            //else
            //    QuotePublish();
        }
    }
}
